package com.study.concurrent.C_ThreadSynchronizationHelperClass.c_ManyRscourcesConcurrentControl;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by 赵超 on 2017/3/25.
 * 文档打印队列
 */
public class PrintQueue {
    /**
     * 创建信号量对象
     */
    private final Semaphore semaphore;

    /**
     * 存放打印机状态的数组，打印机被占用false，打印机未被占用true
     */
    private boolean[] freePrinters;

    /**
     * 锁对象lockPrinters，用来保护对freePrinters数组的访问
     */
    private Lock lockPrinters;

    /**
     * 初始化信号量 new Semaphore(3)（允许三个线程同时访问资源）
     * 初始化打印机状态为 true
     * 初始化锁lock为实现类 ReentrantLock
     */
    public PrintQueue() {
        this.semaphore = new Semaphore(3);
        this.freePrinters = new boolean[3];
        for (int i = 0; i < 3; i++) {
            freePrinters[i] = true;
        }
        lockPrinters = new ReentrantLock();
    }

    /**
     * 文档打印工作
     *
     * @param document 待打印文档
     */
    public void printJob(Object document) {
        try {
            // 获取信号量，最开始调用acquire()方法的3个线程将获得对临界区的访问权限
            semaphore.acquire();
            // 获取打印机编号
            int assignedPrinter = getPrinter();
            long duration = (long) (Math.random() * 10);
            System.out.printf("%s 使用打印机%d 执行打印%s 打印周期 %d 秒\n",
                    Thread.currentThread().getName(), assignedPrinter, document.toString(), duration);
            TimeUnit.SECONDS.sleep(duration);
            // 执行完成打印工作之后将状态设置为true，可以执行新的打印工作
            freePrinters[assignedPrinter] = true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 关闭信号量
            semaphore.release();
        }
    }

    /**
     * 获取打印编号，更改打印机状态为false
     */
    private int getPrinter() {
        int ret = -1;
        try {
            lockPrinters.lock();
            for (int i = 0; i < freePrinters.length; i++) {
                if (freePrinters[i]) {
                    ret = i;
                    // 将打印机状态设置为false，说明打印机已占用
                    freePrinters[i] = false;
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lockPrinters.unlock();
        }
        return ret;
    }
}
